import java.io.File;
import java.io.FilenameFilter;
import java.util.*;

/**
 * Represents text corpus as sequence of articles.
 */
public class Corpus
{
    /**
     * Sequence of articles.
     */
    public ArrayList<Article> articles = new ArrayList<Article>();

    /**
     * Construct empty corpus.
     */
    public Corpus()
    { }

    /**
     * Construct from all articles in given directory (*.xml files).
     */
    public Corpus(String directoryPath) throws Throwable
    {
        File dir = new File(directoryPath);
        assert dir.isDirectory();

        // filter for XML files
        FilenameFilter filter = new FilenameFilter()
        {
            public boolean accept(File dir, String name)
            {
                return name.endsWith(".xml");
            }
        };

        // add all XML articles from the directory
        File[] articleFiles = dir.listFiles(filter);
        for (File articleFile : articleFiles)
            articles.add(new Article(articleFile));
    }

    public void LearnRules(Collection<Rule> rules)
    {
        for (Article article : articles)
            for (Sentence sentence : article.sentences)
                Rule.LearnAll(rules, sentence.tokens);
    }

    /**
     * Simulate applying rule to the corpus.
     */
    public Rule.TestResult TestRule(Rule rule)
    {
        Rule.TestResult result = new Rule.TestResult();
        for (Article article : articles)
            for (Sentence sentence : article.sentences)
            {
                Rule.TestResult singleResult = rule.Test(sentence.tokens);
                result.Add(singleResult);
            }
        return result;
    }

    /**
     * Apply rule to corpus and learn new rules.
     * @param newRules collection of rules to add newly learned rules into.
     * Ignored if null.
     */
    public Rule.TestResult ApplyRule(Rule rule, Collection<Rule> newRules)
    {
        Rule.TestResult result = new Rule.TestResult();
        for (Article article : articles)
            for (Sentence sentence : article.sentences)
            {
                Rule.TestResult singleResult = rule.Apply(sentence.tokens, newRules);
                result.Add(singleResult);
            }
        return result;
    }

    /**
     * Split into training and evaluation corpora.
     */
    public void Split(Corpus trainingCorpus, Corpus EvaluationCorpus, double ratio, Random random,
                      boolean byErrors)
    {
        assert 0 < ratio && ratio < 1;
        ArrayList<Article> allArticles = new ArrayList<Article>(this.articles);
        Collections.shuffle(allArticles, random);
        Stats stats = CountStats();
        int total = byErrors ? stats.error : stats.total;
        int current = 0;
        int splitIndex = 0;
        while (current < total * ratio)
        {
            Stats currStats = allArticles.get(splitIndex++).CountStats();
            current += byErrors ? currStats.error : currStats.total;
        }

        trainingCorpus.articles.addAll(allArticles.subList(0, splitIndex));
        EvaluationCorpus.articles.addAll(allArticles.subList(splitIndex, allArticles.size()));
    }

    /**
     * Count token error statistics.
     */
    public Stats CountStats()
    {
        Stats stats = new Stats();
        for (Article article : articles)
            stats.Add(article.CountStats());
        return stats;
    }
}